package com.cskaoyan.javase._2inner._6lambda._0introduction;

/**
 * Lambda 表达式是 JDK8 的一个新特性
 *      可以取代接口的匿名内部类，写出更优雅的Java 代码。
 * 如果说匿名内部类实际上是局部内部类的更进一步，简化了局部内部类
 * 那么Lambda就是匿名内部类更进一步，语法上更简洁了
 * 代码更优雅了，是高端的玩法，是人上人的玩法。
 *
 * 以上描述总体就说了三点：
 *      1.Lambda表达式仍然是局部内部类，是特殊的局部内部类，仍然定义在局部位置。
 *              而且局部内部类的注意事项，也一样存在。
 *
 *      2.匿名内部类取代局部内部类,是取代局部内部类的继承以及实现
 *          Lambda表达式在取代匿名内部类时，不是全部都取代，而是取代接口的匿名内部类
 *          而类的匿名内部类Lambda表达式是不能取代的。
 *
 *      3. Lambda表达式是匿名内部类的更进一步，Lambda表达式得到的也不是一个类
 *          而是一个对象，并且是接口的子类对象。
 *
 *
 * Lambda表达式的语法:
 * 1.前提条件
 *      Lambda表达式取代接口匿名内部类时,并不是所有的接口匿名内部类都能取代
 *          而是要求接口必须是一个"功能接口"(functional interface)
 *      功能接口: 指的是接口中,有且仅有一个必须强制子类实现的抽象方法
 *      在Java中,功能接口使用注解"@FunctionalInterface"标记它,如果被标记的接口没报错,那么它就是功能接口
 *      思考:
 *          a.功能接口中就只有一个方法吗?
 *              不是,在Java8以后,接口中还可以定义默认方法以及静态方法
 *              它们不需要子类实现
 *
 *          b.功能接口中就只能有一个抽象方法吗?
 *              不是
 *              实际上有些抽象方法,是不需要强制子类实现的
 *              Object类是Java中所有类的父类,而Object类当中是存在很多成员方法的
 *              如果一个抽象方法可以用Object类当中的方法作为默认实现,那么该抽象方法就不再强制子类实现
 *
 *          当然,开发中绝大多数功能接口都是只有一个方法的,就是那个必须要子类实现的抽象方法.
 *
 * 以上,功能接口准备完毕后,就可以按照以下语法写Lambda表达式:
 * 语法:
 *      () -> {}
 * 解释:
 *      1."()"是功能接口中那个必须要实现的抽象方法的,形参列表
 *          必须把形参列表完整抄下来,如果方法是无参的,保持空着即可
 *      2."->"是Lambda表达式的运算符,由一个横杠"-"和大于号">"组成
 *          读作"goes to"
 *      3."{}"是功能接口中那个必须要实现的抽象方法的,重写方法体
 *          既然是方法体,那在里面定义的变量就是局部变量.
 *          既然是方法体,那么Lambda表达式实现的功能接口的子类对象,是不能新增自身成员的.
 *      思考:
 *          为什么Lambda表达式要求功能接口,有且只有一个必须强制子类实现的抽象方法?
 *          因为Lambda表达式的语法结构中必须重写一个方法,也只能重写一个方法.
 *
 * 直接在代码中写上述语法,肯定会报错
 * 原因是直接写该语法,编译器是无从判断该Lambda表达式创建的对象,究竟是哪个功能接口的子类对象
 *
 * 于是为了不编译报错,需要告诉编译器Lambda表达式究竟创建的是谁的子类对象,这个过程称之为"Lambda表达式的类型推断"
 *
 * Lambda表达式的类型推断如何完成呢?
 *      需要额外信息,帮助编译器完成类型推断
 *      具体来说就是需要根据上下文代码完成类型推断
 * 有以下方式:
 *      1.直接用功能接口的引用接收它
 *          在使用匿名内部类时,如果直接使用引用接收
 *          会导致无法访问子类独有成员
 *          但Lambda表达式没有这个问题,因为它不能定义独有成员.
 *
 *      2.使用Lambda表达式类型推断的特殊语法,类似于强转.(了解)
 *      (功能接口的名字)(Lambda表达式)
 *      表示Lambda表达式是前面括号中功能接口的子类对象
 *      注意上述语法必须直接使用该功能接口子类对象
 *      否者会报错
 *      这种语法比较复杂,不够直观,实际开发中很少见,知道即可.
 *
 *      实际上上述两种推断形式都不是最常用的
 *      最常用的是下面两种:
 *          借助方法来完成类型推断
 *      3.借助方法的返回值类型,完成类型推断
 *      4.借助方法的形参数据类型,完成类型推断
 *
 * 以上就是Lambda表达式的语法实现
 *
 * @since 11:00
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // 用Lambda表达式创建功能接口IDemo的子类对象
        IDemo id = () -> {
            System.out.println("Lambda表达式高级!");
        };
        id.test();

        ((IDemo) (() -> {
            System.out.println("我今天中午吃西瓜!");
        })).test();

        System.out.println("-------------------");
        I2 i = method();
        System.out.println(i.test(666));

        System.out.println("-------------------");
        method((double a) -> {
            System.out.println(a);
            return "Hello World!";
        });
    }

    public static I2 method() {
        // 这里必然返回一个接口I2的子类对象
        return (int a) -> {
            System.out.println(a);
            return a + 100;
        };
    }

    public static void method(I3 i) {
        System.out.println(i.test(0.1));
    }
}

@FunctionalInterface
interface I3 {
    Object test(double a);
}

@FunctionalInterface
interface I2 {
    int test(int a);
}

@FunctionalInterface
interface IDemo {
    void test();
}

@FunctionalInterface
interface IA {
    void test();

    default void test2() {
    }

    static void test3() {
    }

    int hashCode();

    boolean equals(Object as);
}

class IAImpl implements IA {
    @Override
    public void test() {
        System.out.println("只需要实现test方法即可!");
    }
}

// @FunctionalInterface
interface IB {
}

// @FunctionalInterface
interface IC {
    void test();

    void test2();
}